"""
OCR认证处理定时任务 - 每秒处理10个任务
使用APScheduler定时执行
"""
import json
import logging
import time
from datetime import datetime
from utils.redis_utils import get_redis
from utils.database import SessionLocal
from Models.login.models import User, RealNameAuth
from Views.home.Profile.AccessToken import GetAccess
import requests
import base64

logger = logging.getLogger(__name__)

baidu_ocr = GetAccess()
BATCH_SIZE = 10  # 每秒最多处理10个任务

# 超时配置
DOWNLOAD_TIMEOUT = 60  # 图片下载超时时间（秒）
OCR_TIMEOUT = 60  # OCR接口调用超时时间（秒）
OCR_CONNECT_TIMEOUT = 30  # OCR连接超时时间（秒）
MAX_RETRIES = 3  # 最大重试次数（总共尝试4次）
RETRY_DELAY = 5  # 重试延迟（秒），给网络更多时间恢复


# 识别身份证信息
def recognize_idcard(image_url: str = None, image_data: str = None) -> dict:
    """
    识别身份证信息
    1. 从URL下载图片（image_url）
    2. 直接使用Base64数据（image_data）
    
    Args:
        image_url: 图片URL（可选）
        image_data: Base64编码的图片数据，支持）
                image_base64 = image_data.split(',', 1)[1]
            else:
                # 直接使用base64字符串
                image_base64 = image_data
            logger.info("Base64数据已准备")
        except Exception as e:
            error_msg = f"处理Base64数据失败：{str(e)}"
            logger.error(error_msg)
            return {"error": error_msg}
    elif image_url:
        # 从URL下载图片（带重试机制）
        logger.info(f"开始处理身份证OCR识别，图片URL: {image_url}")
        # 创建Session对象以复用连接
        session = requests.Session()
        requests.packages.urllib3.disable_warnings()
        
        for attempt in range(MAX_RETRIES + 1):
            try:
                logger.info(f"正在从URL下载图片... (尝试 {attempt + 1}/{MAX_RETRIES + 1})")
                response = session.get(image_url, timeout=DOWNLOAD_TIMEOUT, verify=False)
                response.raise_for_status()
                logger.info(f"图片下载成功，大小: {len(response.content)} bytes")
                
                # 将图片转换为Base64（百度OCR要求）
                image_base64 = base64.b64encode(response.content).decode("utf-8")
                logger.info("图片已转换为Base64格式")
                session.close()
                break  # 成功则跳出重试循环
            except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
                if attempt < MAX_RETRIES:
                    logger.warning(f"下载图片超时或连接失败（尝试 {attempt + 1}/{MAX_RETRIES + 1}），{RETRY_DELAY}秒后重试: {str(e)}")
                    time.sleep(RETRY_DELAY)
                else:
                    error_msg = f"从URL下载图片失败（已重试{MAX_RETRIES}次）：{str(e)}"
                    logger.error(error_msg)
                    session.close()
                    return {"error": error_msg}
            except Exception as e:
                error_msg = f"从URL下载图片失败：{str(e)}"
                logger.error(error_msg)
                session.close()
                return {"error": error_msg}
    else:
        error_msg = "未提供图片URL或Base64数据"
        logger.error(error_msg)
        return {"error": error_msg}

    # 第三步：获取百度OCR访问令牌
    access_token = baidu_ocr.get_access_token()
    if not access_token:
        error_msg = "获取百度OCR访问令牌失败"
        logger.error(error_msg)
        return {"error": error_msg}
    logger.info("已获取百度OCR访问令牌")

    # 第四步：调用百度OCR接口进行身份证识别（带重试机制）
    # 创建Session对象以复用连接，提高性能
    session = requests.Session()
    # 设置默认超时和重试适配器
    from requests.adapters import HTTPAdapter
    from urllib3.util.retry import Retry
    
    retry_strategy = Retry(
        total=0,  # 不在适配器层面重试，由我们自己控制重试
        backoff_factor=0,
        status_forcelist=[],
    )
    adapter = HTTPAdapter(max_retries=retry_strategy)
    session.mount("https://", adapter)
    session.mount("http://", adapter)
    
    url = f"https://aip.baidubce.com/rest/2.0/ocr/v1/idcard?access_token={access_token}"
    data = {"image": image_base64, "id_card_side": "front"}
    headers = {"Content-Type": "application/x-www-form-urlencoded", "Accept": "application/json"}

    ocr_result = None
    last_error = None
    for attempt in range(MAX_RETRIES + 1):
        try:
            logger.info(f"正在调用百度OCR接口... (尝试 {attempt + 1}/{MAX_RETRIES + 1})")
            # 使用更长的超时时间，分别设置连接和读取超时
            # 连接超时：30秒，读取超时：60秒
            ocr_response = session.post(
                url, 
                data=data, 
                headers=headers, 
                timeout=(OCR_CONNECT_TIMEOUT, OCR_TIMEOUT)  # (连接超时, 读取超时)
            )
            ocr_response.raise_for_status()
            ocr_result = ocr_response.json()
            logger.info(f"百度OCR接口调用完成")
            break  # 成功则跳出重试循环
        except (requests.exceptions.Timeout, requests.exceptions.ConnectionError) as e:
            last_error = e
            if attempt < MAX_RETRIES:
                logger.warning(f"OCR接口调用超时或连接失败（尝试 {attempt + 1}/{MAX_RETRIES + 1}），{RETRY_DELAY}秒后重试: {str(e)}")
                time.sleep(RETRY_DELAY)
                # 重新获取access_token（避免token过期）
                access_token = baidu_ocr.get_access_token()
                if not access_token:
                    error_msg = "重试时获取百度OCR访问令牌失败"
                    logger.error(error_msg)
                    session.close()
                    return {"error": error_msg}
                url = f"https://aip.baidubce.com/rest/2.0/ocr/v1/idcard?access_token={access_token}"
            else:
                error_msg = f"调用百度OCR接口失败（已重试{MAX_RETRIES}次）：{str(last_error)}"
                logger.error(error_msg)
                session.close()
                return {"error": error_msg}
        except Exception as e:
            error_msg = f"调用百度OCR接口失败：{str(e)}"
            logger.error(error_msg)
            session.close()
            return {"error": error_msg}
    
    # 关闭session
    session.close()
    
    if ocr_result is None:
        error_msg = "OCR接口调用失败，未获得结果"
        logger.error(error_msg)
        return {"error": error_msg}

    # 第五步：处理OCR识别结果
    if "error_code" in ocr_result:
        error_msg = f"OCR识别失败：{ocr_result.get('error_msg', '未知错误')}"
        logger.error(error_msg)
        return {"error": error_msg}

    words_result = ocr_result.get("words_result", {})
    result = {
        "name": words_result.get("姓名", {}).get("words", "").strip(),
        "id_card": words_result.get("公民身份号码", {}).get("words", "").strip(),
        "address": words_result.get("住址", {}).get("words", "").strip()
    }
    logger.info(f"OCR识别成功，提取信息: 姓名={result['name']}, 身份证号={result['id_card'][:6]}***")

    return result




# 处理认证任务
# 参数：task_data: 任务数据，包含task_id/user_id/image_url/image_data
def process_single_task(task_data: dict, redis_client):
    """处理单个认证任务"""
    task_id = task_data.get("task_id")
    user_id = task_data.get("user_id")
    image_url = task_data.get("image_url")
    image_data = task_data.get("image_data")

    db = SessionLocal()
    try:
        # 更新状态为处理中
        redis_client.hset(f"RZ:{user_id}:idcard_ocr_tasks", task_id, json.dumps({
            "status": "processing",
            "message": "正在识别身份证信息...",
            "timestamp": datetime.now().isoformat()
        }))

        # OCR识别（支持URL或Base64数据）
        ocr_result = recognize_idcard(image_url=image_url, image_data=image_data)

        #调用上方的recognize_idcard 函数，识别身份证信息
        if "error" in ocr_result:
            redis_client.hset(f"RZ:{user_id}:idcard_ocr_tasks", task_id, json.dumps({
                "status": "failed",
                "message": ocr_result["error"]
            }))
            # 发送WebSocket通知
            send_ws_notification(redis_client, user_id, task_id, "failed", ocr_result["error"])
            return

        if not ocr_result.get("name") or not ocr_result.get("id_card"):
            error_msg = "身份证信息识别不完整"
            redis_client.hset(f"RZ:{user_id}:idcard_ocr_tasks", task_id, json.dumps({
                "status": "failed",
                "message": error_msg,
                "timestamp": datetime.now().isoformat()
            }))
            send_ws_notification(redis_client, user_id, task_id, "failed", error_msg)
            return

        # 存储认证结果
        user = db.query(User).filter(User.id == user_id).first()
        if not user:
            error_msg = "用户不存在"
            redis_client.hset(f"RZ:{user_id}:idcard_ocr_tasks", task_id, json.dumps({
                "status": "failed",
                "message": error_msg,
                "timestamp": datetime.now().isoformat()
            }))
            send_ws_notification(redis_client, user_id, task_id, "failed", error_msg)
            return

        # 检查是否已认证
        if user.is_verified == 1:
            error_msg = "用户已经完成实名认证"
            redis_client.hset(f"RZ:{user_id}:idcard_ocr_tasks", task_id, json.dumps({
                "status": "failed",
                "message": error_msg,
                "timestamp": datetime.now().isoformat()
            }))
            send_ws_notification(redis_client, user_id, task_id, "failed", error_msg)
            return

        # 如果使用Base64，image_url可能为空，设置为空字符串
        image_url_for_db = image_url if image_url else ""
        
        verification = RealNameAuth(
            user_id=user_id,
            idcard_image_url=image_url_for_db,
            real_name=ocr_result["name"],
            id_number=ocr_result["id_card"],
            address=ocr_result.get("address", "")
        )
        db.add(verification)
        user.is_verified = 1
        db.commit()
        db.refresh(verification)

        # 更新Redis状态
        verify_time = verification.created_at if hasattr(verification, "created_at") else datetime.now()
        result_data = {
            "status": "success",
            "message": "实名认证成功",
            "timestamp": verify_time.isoformat() if hasattr(verify_time, "isoformat") else str(verify_time),
            "data": {
                "id": verification.id,
                "name": verification.real_name,
                "id_card": verification.id_number,
                "verify_time": str(verify_time)
            }
        }
        redis_client.hset(f"RZ:{user_id}:idcard_ocr_tasks", task_id, json.dumps(result_data))

        # 发送WebSocket通知
        send_ws_notification(redis_client, user_id, task_id, "success", "实名认证成功！", result_data.get("data"))

    except Exception as e:
        db.rollback()
        error_msg = f"处理失败：{str(e)}"
        logger.error(f"处理任务失败: {error_msg}", exc_info=True)
        redis_client.hset(f"RZ:{user_id}:idcard_ocr_tasks", task_id, json.dumps({
            "status": "failed",
            "message": error_msg
        }))
        send_ws_notification(redis_client, user_id, task_id, "failed", error_msg)
    finally:
        db.close()


def send_ws_notification(redis_client, user_id: int, task_id: str, status: str, message: str, data=None):
    """发送WebSocket通知到用户（使用统一的通知工具）"""
    from Views.home.websocket.ws_notifier import notify_verify_result
    notify_verify_result(user_id, task_id, status, message, data)


def process_batch():
    """批量处理任务 - 每秒处理最多10个（从全局队列统一读取）"""
    redis_client = get_redis()  # 获取Redis客户端
    processed_count = 0  # 已处理的任务数

    try:
        # 从全局队列统一读取任务（效率更高，限流更准确）
        while processed_count < BATCH_SIZE:
            task_json = redis_client.lpop("idcard_ocr_queue")
            if not task_json:
                break  # 队列为空，退出

            try:
                task_data = json.loads(task_json)
                process_single_task(task_data, redis_client)
                processed_count += 1
                logger.info(f"处理任务成功: task_id={task_data.get('task_id')}, user_id={task_data.get('user_id')}")
            except Exception as e:
                logger.error(f"处理任务失败: {e}", exc_info=True)

        if processed_count > 0:
            logger.info(f"本次处理了 {processed_count} 个任务")
    except Exception as e:
        logger.error(f"批量处理任务出错: {e}", exc_info=True)

